package org.zjvis.datascience.service.snapshot;

import lombok.Builder;
import org.apache.commons.collections4.queue.CircularFifoQueue;

import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class SnapshotManager<Element> {
    public static class SnapshotException extends Exception {
        public SnapshotException(String message) {
            super(message);
        }

        public SnapshotException(String message, Throwable cause) {
            super(message, cause);
        }
    }

    private int currentIndex;
    private final CircularFifoQueue<Element> snapshotQueue;

    @Builder
    public SnapshotManager(int capacity) {
        this.currentIndex = -1;
        this.snapshotQueue = new CircularFifoQueue<>(capacity);
    }

    public boolean notSnapshotBefore() {
        return -1 == this.currentIndex;
    }

    public void snapshot(Element contentList) {
        // remove snapshots newer than current
        snapshotQueue.removeAll(IntStream.range(currentIndex + 1, snapshotQueue.size())
                .mapToObj(snapshotQueue::get)
                .collect(Collectors.toList())
        );
        snapshotQueue.offer(contentList);
        currentIndex = snapshotQueue.size() - 1;
    }

    public boolean undoAble() {
        return currentIndex > 0;
    }

    public void undo() throws SnapshotException {
        if (!undoAble()) {
            throw new SnapshotException(String.format(
                    "not able to act undo operation(currentIndex=%s, snapshotQueueSize=%s)",
                    currentIndex, snapshotQueue.size()));
        }
        currentIndex--;
    }

    public boolean redoAble() {
        return currentIndex < snapshotQueue.size() - 1;
    }

    public void redo() throws SnapshotException {
        if (!redoAble()) {
            throw new SnapshotException(String.format(
                    "not able to act redo operation(currentIndex=%s, snapshotQueueSize=%s)",
                    currentIndex, snapshotQueue.size()));
        }
        currentIndex++;
    }

    public Element current() {
        return snapshotQueue.get(currentIndex);
    }
}

